package demo.auth.service.impl;

import demo.auth.config.JwtPropertiesConfig;
import demo.auth.service.AuthService;
import demo.common.auth.entity.Payload;
import demo.common.auth.entity.UserInfo;
import demo.common.auth.utils.JwtUtils;
import demo.common.exception.SnowException;
import demo.common.exception.SnowExceptionEnum;
import demo.common.utils.CookieUtils;
import demo.user.client.UserClient;
import demo.user.dto.StatusDTO;
import demo.user.dto.UserDTO;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class AutoServiceImpl implements AuthService {

    @Autowired
    private UserClient userClient;

    @Autowired
    private JwtPropertiesConfig jwtPropertiesConfig;
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 用户登录
     * @param userName
     * @param password
     * @param response
     */
    @Override
    public void login(String userName, String password, HttpServletResponse response) {
        // 调用用户中心微服务，查询用户信息
        UserDTO userDTO = userClient.userLogin(userName, password);
        if (userDTO == null) {
            throw new SnowException(SnowExceptionEnum.INVALID_USERNAME_PASSWORD);
        }
        // 获取用户权限
        StatusDTO userStatus = userClient.getUserStatus(userDTO.getId());
        if (userStatus == null) {
            // 用户没有权限
            throw new SnowException(SnowExceptionEnum.USER_RIGHTS_INSUFFICIENT);
        }
        // 基于JWT生成Token
        UserInfo userInfo = new UserInfo(userDTO.getId(), userDTO.getUsername(), userStatus.getId());
        // 生成Token，并返回给浏览器端操作
        createToken(response, userInfo);
    }

    /**
     * 查询用户信息
     * @param request
     * @param response
     * @return
     */
    @Override
    public UserInfo verifyUser(HttpServletRequest request, HttpServletResponse response) {
        // 获取cookie中的token信息
        String token = CookieUtils.getCookieValue(request, jwtPropertiesConfig.getUser().getCookieName());
        // 解析token，如果成功返回用户信息
        try {
            Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtPropertiesConfig.getPublicKey(), UserInfo.class);
            UserInfo userInfo = payload.getUserInfo();
            // 判断redis中记录的失效token黑名单中，使用存在当前Token
            Boolean boo = redisTemplate.hasKey(payload.getId());
            if (boo) {
                throw new SnowException(SnowExceptionEnum.UNAUTHORIZED);
            }
            // 维护Token有效，当页面刷新后，举例Token过期时间不足5分钟重新生成
            Date expiration = payload.getExpiration();
            // 获取token开始重新生成的时间(刷新时间)
            DateTime refreshTime = new DateTime(expiration).minusMinutes(jwtPropertiesConfig.getUser().getMinRefreshInterval());
            // 当时时间已经超过token开始生成的时间，需重新生成Token
            if (refreshTime.isBefore(System.currentTimeMillis())) {
                createToken(response, userInfo);
            }
            return userInfo;
        } catch (Exception e) {
            e.printStackTrace();
            throw new SnowException(SnowExceptionEnum.UNAUTHORIZED);
        }
    }

    /**
     * 退出登录
     * @param request
     * @param response
     */
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response) {
        // 获取token
        String token = CookieUtils.getCookieValue(request, jwtPropertiesConfig.getUser().getCookieName());
        //2、解析token
        Payload<UserInfo> payload = JwtUtils.getInfoFromToken(token, jwtPropertiesConfig.getPublicKey(), UserInfo.class);
        String tokenId = payload.getId();
        //退出登录的时间距离token过期时间的剩余时间
        //long time = payload.getExpiration().getTime() - new Date().getTime();
        long time = payload.getExpiration().getTime() - System.currentTimeMillis();
        //3、将token记录到redis的失效黑名单中，仅需要保存退出登录的时间距离token过期时间的剩余时间即可
        if(time>5000){
            redisTemplate.boundValueOps(tokenId).set("",time, TimeUnit.MILLISECONDS);
        }

        //4、删除cookie操作
        CookieUtils.deleteCookie(jwtPropertiesConfig.getUser().getCookieName(),jwtPropertiesConfig.getUser().getCookieDomain(),response);
    }

    /**
     * 生成Token并返回给浏览器端
     * @param response
     * @param userInfo
     */
    private void createToken(HttpServletResponse response, UserInfo userInfo) {
        String token = JwtUtils.generateTokenExpireInMinutes(userInfo, jwtPropertiesConfig.getPrivateKey(), jwtPropertiesConfig.getUser().getExpire());
        // 将token保存到cookie中，并返回给浏览器
        CookieUtils.newCookieBuilder()
                .response(response)
                .domain(jwtPropertiesConfig.getUser().getCookieDomain())
                .name(jwtPropertiesConfig.getUser().getCookieName())
                .value(token)
                .httpOnly(true) // 是否允许浏览器通过js代码修改cookie
                .build();
    }
}
